package com.ndp.fb.util;

import com.ndp.fb.rdb.annotation.*;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 反射工具类 ,没有考虑到方法的重载（over-write）。因为复写(over-load)的话，没有问题
 *
 * @author maczam@163.com
 */
public class ReflectUtil {
    private static Logger logger = LoggerFactory.getLogger(ReflectUtil.class);

    /**
     * 查找对象声明字段，递归支持多重继承
     * @param clazz
     * @return
     */
    private static Map<Class, List<Field>> GetFieldsRecurseMap = new HashMap<>();

    public static List<Field> getFieldsRecurse(Class clazz) {
        List<Field> list = GetFieldsRecurseMap.get(clazz);
        if (list == null) {
            list = new ArrayList<>();
            for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
                Field[] superFields = c.getDeclaredFields();
                Collections.addAll(list, superFields);
            }
            GetFieldsRecurseMap.put(clazz, list);
        }
        return list;
    }

    /**
     * 分析保存对象，返回不要插入的字段和插入的值
     * @param clazz
     * @return
     */

    private static Map<Class,List<InsertValuePair>> InsertValuePairMap = new HashMap<>();
    public static List<InsertValuePair> getInsertValuePairList(Class clazz) {
        List<InsertValuePair> insertParams = InsertValuePairMap.get(clazz);
        if(insertParams == null){
            insertParams =  new ArrayList<>();
            List<Field> list = ReflectUtil.getFieldsRecurse(clazz);
            for (Field field : list) {
                if (field.getName().equalsIgnoreCase("SerialVersionUID")) {
                    continue;
                }
                NoSave trans = field.getAnnotation(NoSave.class);
                if (trans != null) {  // 不持久化
                    continue;
                }
                Id annId = field.getAnnotation(Id.class);
                if (annId != null) { // insert 不需要 id
                    continue;
                }
                Class<?> type;
                Column col = field.getAnnotation(Column.class);
                if (col != null) {
                    type=getAnnoColumnType(field);
                }else {
                    type = field.getType();
                }

                String methodName = "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
                Method method = getMethod(clazz,methodName,type,field);

                //参数进行预处理
                InsertValuePair insertValuePair = new InsertValuePair();
                String annoColumnName = ReflectUtil.getAnnoColumnName(field);
                insertValuePair.setField(field);
                insertValuePair.setMethod(method);
                insertValuePair.setColumnName(annoColumnName);
                insertValuePair.setColumn(col);
                insertParams.add(insertValuePair);
            }
            InsertValuePairMap.put(clazz, insertParams);
        }
        return insertParams;
    }

    public static void setFieldValue(Object obj, Method method, Field field, Object value) {
        if(value == null)
            return;
        try {
            if(method == null){
                field.setAccessible(true);
                field.set(obj,value);
            } else  {
                method.invoke(obj, value);
            }
        } catch (Exception e) {
            System.err.println("field>>>>" + field + ",method>>>" + method + ";value>>>" + value);
            throw new RuntimeException(e);
        }
    }

    public static Object getFieldValue(Object obj, Method method, Field field) {
        try {
            if (method != null) {
                return method.invoke(obj);
            } else {
                field.setAccessible(true);
                return field.get(obj);
            }
        } catch (Exception e) {
            System.err.println("field>>>>" + field + ",method>>>" + method);
            throw new RuntimeException(e);
        }
    }


    /**
     * 获取Column 注解配置的数据库字段名称，只是标记没有配置的话取 字段名的toSnakeCase。
     *
     * @param field
     * @return
     */
    public static String getAnnoColumnName(Field field) {
        Column column = field.getAnnotation(Column.class);
        String annoColumnName = null;
        if (column != null) {
            //从注解中取
            annoColumnName = column.value();
        }
        //没有注解或注解中没有设置
        if (annoColumnName == null || "".equals(annoColumnName)) {
            annoColumnName = CamelCaseUtils.toSnakeCase(field.getName());
        }
        return annoColumnName;
    }

    public static Class getAnnoColumnType(Field field) {
        Class paramType;
        Column annotation = field.getAnnotation(Column.class);
        if (annotation == null) {
            paramType = field.getType();
        } else {
            ColumnType type = annotation.type();
            switch (type) {
                case Null:
                    paramType = field.getType();
                    break;
                case Varchar:
                    paramType = String.class;
                    break;
                case Boolean:
                    paramType = Boolean.class;
                    break;
                case Int:
                    paramType = Integer.class;
                    break;
                case Long:
                    paramType = Long.class;
                    break;
                case Double:
                    paramType = Double.class;
                    break;
                case Date2Long:
                    paramType = Long.class;
                    break;
                case Date:
                    paramType = Date.class;
                    break;
                case Time:
                    paramType= Date.class;
                    break;
                default:
                    paramType = String.class;
            }
        }
        return paramType;
    }
    public static Method getMethod(Class<?> clazz, String methodName, Class type, Field field) {
        //默认的 包装类型Set方法
        boolean isSetter = methodName.startsWith("set");
        Method method = getGetterOrSetter(clazz, methodName, type, isSetter);

        if (method == null) {
            if (type == Boolean.class) {
                if (!field.getName().startsWith("is")) {
                    // 不是以is开始，直接试小类型。
                    type=boolean.class;
                } else {
                    //is开始
                    methodName = methodName.replace("Is", "");
                    //大类型
                    type=Boolean.class;
                    method = getGetterOrSetter(clazz, methodName, type, isSetter);
                    if (method == null) {
                        type=boolean.class;
                    }
                }
            } else if (type == Integer.class) {
                type=int.class;
            } else if (type == Long.class) {
                type=long.class;
            } else if (type == Double.class) {
                type=double.class;
            }
        }
        //Boolean is 可能能找到，就不在取了。
        if (method == null) {
            method=getGetterOrSetter(clazz, methodName, type, isSetter);
        }
        return method;
    }

    private static Method getGetterOrSetter(Class<?> clazz, String methodName, Class type, boolean isSetter) {
        Method method;
        if (isSetter) {
            method = MethodUtils.getAccessibleMethod(clazz, methodName, type);
        }else {
            method = MethodUtils.getAccessibleMethod(clazz, methodName);
        }
        return method;
    }

    public static <T> List<T>   getMockInstancelist(Class<T> clazz,int size) {
         List<T> list=new ArrayList<>();
        for (int i = 0; i < size; i++) {
            list.add(getMockInstance(clazz));
        }
         return list;
    }

    public static <T> T getMockInstance(Class<T> clazz) {
        T t = null;
        try {
            t = clazz.newInstance();
            List<Field> fields = getFieldsRecurse(clazz);
            for (Field field : fields) {
                if (field.getName().equalsIgnoreCase("serialVersionUID")) {
                    continue;
                }
                String methodName = "set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
                Class<?> type = field.getType();
                Method method = ReflectUtil.getMethod(clazz, methodName, field.getType(), field);
                Object value = null;
                if (type == String.class) {
                    value = String.valueOf(RandomUtils.nextInt(99,199));
                } else if (type == Boolean.class) {
                    value = false;
                } else if (type == Integer.class) {
                    value = RandomUtils.nextInt(1,99);
                } else if (type == Long.class) {
                    value = RandomUtils.nextLong(99999,100000000);
                } else if (type == Double.class) {
                    value = RandomUtils.nextDouble(10000,999999);
                } else if (type == Date.class) {
                    value = new Date();
                }
                if (value != null) {
                    ReflectUtil.setFieldValue(t, method, field, value);
                }else {
                    logger.error("未知的字段类型：{}。请手动处理。",field.getName());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return t;
    }



    /**
     * 获取tableName
     * @param clazz
     * @return
     */
    public static String getTableName(Class<?> clazz) {
        String tableName;
        Table tableAnnotation = clazz.getAnnotation(Table.class);
        if (tableAnnotation != null) {
            //使用注解设置的表名称
            tableName = tableAnnotation.value();
        } else {
            //使用默认表名称
            tableName = CamelCaseUtils.toSnakeCase(clazz.getSimpleName());
        }
        return tableName;
    }
}
